package com.ae.base.placed;

import com.ae.api.IPlaced;
import com.ae.base.ModConfig;
import com.ae.base.ModConstant;
import com.ae.base.block.PlacedItem;
import com.ae.base.block.PlacedItemTile;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.math.vector.Quaternion;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraft.util.math.vector.Vector3i;

import javax.annotation.Nonnull;

public class ItemPlaced implements IPlaced {
    private final VoxelShape shape1 = Block.box(0, 0, 0, 16, 2, 16);
    private final VoxelShape shape2 = Block.box(0, 0, 0, 16, 4, 16);
    private final VoxelShape shape3 = Block.box(0, 0, 0, 16, 6, 16);
    private final VoxelShape shape4 = Block.box(0, 0, 0, 16, 8, 16);
    private final VoxelShape shape5 = Block.box(0, 0, 0, 16, 10, 16);
    private final VoxelShape shape6 = Block.box(0, 0, 0, 16, 12, 16);
    private final VoxelShape shape7 = Block.box(0, 0, 0, 16, 14, 16);
    private final VoxelShape shape8 = Block.box(0, 0, 0, 16, 16d, 16);
    private final VoxelShape bottom = Block.box(0, 0, 0, 16, 0.5, 16);
    private final VoxelShape wall1 = Block.box(0, 0, 0, 16, 16, 0.5);
    private final VoxelShape wall2 = Block.box(0, 0, 0, 0.5, 16, 16);
    private final VoxelShape wall3 = Block.box(0, 0, 15.5, 16, 16, 16);
    private final VoxelShape wall4 = Block.box(15.5, 0, 0, 16, 16, 16);
    private final VoxelShape box = VoxelShapes.or(bottom, wall1, wall2, wall3, wall4);

    @Override
    public String getId() {
        return ModConstant.ITEM_PLACED;
    }

    @Override
    public VoxelShape getShape(int count) {
        int a = count / 8;
        int f = a * 8 == count ? a : a + 1;

        switch (f) {
            case 1:
                return shape1;
            case 2:
                return VoxelShapes.or(box, shape2);
            case 3:
                return VoxelShapes.or(box, shape3);
            case 4:
                return VoxelShapes.or(box, shape4);
            case 5:
                return VoxelShapes.or(box, shape5);
            case 6:
                return VoxelShapes.or(box, shape6);
            case 7:
                return VoxelShapes.or(box, shape7);
            default:
                return VoxelShapes.or(box, shape8);
        }
    }

    @Override
    public void render(int color, @Nonnull PlacedItemTile tile, float partialTicks, @Nonnull MatrixStack matrixStack, @Nonnull IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) {
        Minecraft mc = Minecraft.getInstance();
        ClientPlayerEntity player = mc.player;

        if (player == null) {
            return;
        }

        Vector3d position = player.position();
        double distance = Math.sqrt(tile.getBlockPos().distSqr(new Vector3i(position.x, position.y, position.z)));

        //超过设置距离就不进行渲染
        if (distance > ModConfig.DISTANCE.get()) {
            return;
        }

        int displayCount = tile.getBlockState().getValue(PlacedItem.COUNT);

        if (displayCount <= 0) {
            return;
        }

        BlockState state = tile.getBlockState();
        ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
        ItemStack stack = tile.handler.getStackInSlot();
        IBakedModel model = itemRenderer.getModel(stack, tile.getLevel(), null);

        if (model.isGui3d()) {
            render3DItems(stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, displayCount, state);
        } else {
            renderItems(stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, displayCount, state);
        }

    }

    private void render3DItems(ItemStack stack, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn, int displayCount, BlockState stateIn) {
        int i = (int) Math.ceil(displayCount / 8d);
        double high = displayCount > 8 ? 0.03125 : 0;
        high = i > 4 ? 0.5 : high;

        if (i < 8) {
            int x;

            for (int j = 1; j <= (i > 4 ? i - 4 : i); j++) {
                x = j % 4 == 0 ? 4 : j % 4;
                renderItem(0.25 * (x % 2 == 0 ? 1 : 3), high + 0.2, 0.25 * (x - 2 > 0 ? 3 : 1), new Quaternion(0, 0, 0, 0), 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            }
        } else {
            Direction face = stateIn.getValue(PlacedItem.DIRECTION);
            switch (face.getOpposite()) {
                case DOWN:
                case UP:
                case NORTH:
                    renderItem3DTO2D(0.2, 0.18, -0.001, new Quaternion(Vector3f.YN, 180, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case SOUTH:
                    renderItem3DTO2D(0.8, 0.18, 1.001, new Quaternion(Vector3f.YN, 0, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case WEST:
                    renderItem3DTO2D(-0.001, 0.18, 0.8, new Quaternion(Vector3f.YN, 90, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case EAST:
                    renderItem3DTO2D(1.001, 0.18, 0.2, new Quaternion(Vector3f.YN, -90, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
            }
        }
    }

    private void renderItems(ItemStack stack, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn, int displayCount, BlockState stateIn) {
        Quaternion quaternion = new Quaternion(90, 0, -30, true);
        if (displayCount <= 8) {
            switch (displayCount) {
                case 8:
                    setQuaternion(110, 5f, -90, quaternion);
                    renderItem(0.4, 0.07, 0.8, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 7:
                    setQuaternion(85, 3, 95, quaternion);
                    renderItem(0.52600625, 0.05, 0.2, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 6:
                    setQuaternion(100, 170, 240, quaternion);
                    renderItem(0.8, 0.03, 0.8, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 5:
                    setQuaternion(90, 5, 110, quaternion);
                    renderItem(0.2, 0.033, 0.6, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 4:
                    setQuaternion(70, 0, 0, quaternion);
                    renderItem(0.7, 0.05, 0.3, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 3:
                    setQuaternion(90.5397f, -5.3154f, -6.9477f, quaternion);
                    renderItem(0.6, 0.044, 0.6, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 2:
                    setQuaternion(90, 0, 0, quaternion);
                    renderItem(0.2, 0.022, 0.2, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 1:
                    setQuaternion(90, 0, -30, quaternion);
                    renderItem(0.5, 0.022, 0.522875, quaternion, 0.8f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            }
        } else if (displayCount <= 32) {
            int i = displayCount / 8;
            setQuaternion(90, 0, 0, quaternion);
            renderItem(0.5, 0.0625 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            switch (i) {
                case 4:
                    setQuaternion(90, 0, 0, quaternion);
                    renderItem(0.5, 0.3125 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 3:
                    setQuaternion(90, 0, 90, quaternion);
                    renderItem(0.5, 0.25 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 2:
                    setQuaternion(90, 0, 0, quaternion);
                    renderItem(0.5, 0.1875 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 1:
                    setQuaternion(90, 0, 90, quaternion);
                    renderItem(0.5, 0.125 + 0.03125, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            }
        } else if (displayCount <= 56) {
            int i = (displayCount - 32) / 8 + 1;
            switch (i) {
                default:
                case 3:
                    setQuaternion(90, 0, 90, quaternion);
                    renderItem(0.5, 0.1875 + 0.03125 + 0.5, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 2:
                    setQuaternion(90, 0, 0, quaternion);
                    renderItem(0.5, 0.125 + 0.03125 + 0.5, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                case 1:
                    setQuaternion(90, 0, 90, quaternion);
                    renderItem(0.5, 0.0625 + 0.03125 + 0.5, 0.5, quaternion, 0.99f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
            }
        } else {
            matrixStack.pushPose();
            Direction face = stateIn.getValue(PlacedItem.DIRECTION);
            switch (face.getOpposite()) {
                case DOWN:
                case UP:
                case NORTH:
                    renderItem(0.2, 0.18, 0.005, new Quaternion(Vector3f.XN, 0, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case SOUTH:
                    renderItem(0.8, 0.18, 0.995, new Quaternion(Vector3f.YN, 180, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case WEST:
                    renderItem(0.005, 0.18, 0.8, new Quaternion(Vector3f.YN, -90, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
                case EAST:
                    renderItem(0.995, 0.18, 0.2, new Quaternion(Vector3f.YN, 90, true), 0.2f, stack, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn);
                    break;
            }
            matrixStack.popPose();
        }
    }

    public void renderItem(double x, double y, double z, Quaternion rotate, float size, ItemStack stack, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) {
        ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
        matrixStack.pushPose();
        matrixStack.translate(x, y, z);
        matrixStack.mulPose(rotate);
        matrixStack.scale(size, size, size);
        IBakedModel model = itemRenderer.getModel(stack, null, null);
        itemRenderer.render(stack, ItemCameraTransforms.TransformType.FIXED, false, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, model);
        matrixStack.popPose();
    }

    public void renderItem3DTO2D(double x, double y, double z, Quaternion rotate, float size, ItemStack stack, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) {
        ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
        matrixStack.pushPose();
        matrixStack.translate(x, y, z);
        matrixStack.mulPose(rotate);
        matrixStack.scale(size, size, 0.001f);
        IBakedModel model = itemRenderer.getModel(stack, null, null);
        itemRenderer.render(stack, ItemCameraTransforms.TransformType.GUI, false, matrixStack, bufferIn, combinedLightIn, combinedOverlayIn, model);
        matrixStack.popPose();
    }

    public void setQuaternion(float x, float y, float z, Quaternion quaternion) {
        x *= ((float) Math.PI / 180F);
        y *= ((float) Math.PI / 180F);
        z *= ((float) Math.PI / 180F);

        float f = (float) Math.sin(0.5F * x);
        float f1 = (float) Math.cos(0.5F * x);
        float f2 = (float) Math.sin(0.5F * y);
        float f3 = (float) Math.cos(0.5F * y);
        float f4 = (float) Math.sin(0.5F * z);
        float f5 = (float) Math.cos(0.5F * z);
        float i = f * f3 * f5 + f1 * f2 * f4;
        float j = f1 * f2 * f5 - f * f3 * f4;
        float k = f * f2 * f5 + f1 * f3 * f4;
        float r = f1 * f3 * f5 - f * f2 * f4;
        quaternion.set(i, j, k, r);
    }
}
